//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2002-2007
// (c) Rafal Fitt (fitt@aplusc.com.pl), 2004
//
// Helpers for printing data in selected format.
//

# include "StdAfx.h"
# include "Resource.h"
# include "PrintUnits.h"
# include "Settings.h"
# include "PtAlgo.h"
# include "fast_floor.h"
# include "ParseHelpers.h"
# include "Globals.h"

# define _NL(_s) _s

elevation_units_t g_ElevationUnit  = unMeters;
distance_units_t  g_DistanceUnit   = unMetric;
area_units_t      g_AreaUnit       = unSquareMeters;
azimuth_t         g_AzimuthFormat  = azDegrees;
posfmt_t          g_PositionFormat = pfMinutes;

const size_t c_cUnitMaxLen = 32;
static TCHAR g_strUnitM       [c_cUnitMaxLen];
static TCHAR g_strUnitFt      [c_cUnitMaxLen];
static TCHAR g_strUnitKm      [c_cUnitMaxLen];
static TCHAR g_strUnitNMile   [c_cUnitMaxLen];
static TCHAR g_strUnitSMile   [c_cUnitMaxLen];
static TCHAR g_strUnitMps     [c_cUnitMaxLen];
static TCHAR g_strUnitFps     [c_cUnitMaxLen];
static TCHAR g_strUnitKmh     [c_cUnitMaxLen];
static TCHAR g_strUnitKnot    [c_cUnitMaxLen];
static TCHAR g_strUnitMph     [c_cUnitMaxLen];
static TCHAR g_strUnitM2      [c_cUnitMaxLen];
static TCHAR g_strUnitKm2     [c_cUnitMaxLen];
static TCHAR g_strUnitFt2     [c_cUnitMaxLen];
static TCHAR g_strUnitMile2   [c_cUnitMaxLen];
static TCHAR g_strUnitAcre    [c_cUnitMaxLen];
static TCHAR g_strUnitHectare [c_cUnitMaxLen];

void InitPrintUnits () {
	g_ElevationUnit  = static_cast<elevation_units_t> (GetSetting (_NL ("OptionElevationUnit"),  (DWORD) unMeters));
	g_DistanceUnit   = static_cast<distance_units_t>  (GetSetting (_NL ("OptionDistanceUnit"),   (DWORD) unMetric));
	g_AreaUnit       = static_cast<area_units_t>      (GetSetting (_NL ("OptionAreaUnit"),       (DWORD) unSquareMeters));
	g_AzimuthFormat  = static_cast<azimuth_t>         (GetSetting (_NL ("OptionAzimuthFormat"),  (DWORD) azDegrees));
	g_PositionFormat = static_cast<posfmt_t>          (GetSetting (_NL ("OptionPositionFormat"), (DWORD) pfMinutes));

	LoadString (IDS_UNIT_M,       g_strUnitM,       c_cUnitMaxLen);
	LoadString (IDS_UNIT_FT,      g_strUnitFt,      c_cUnitMaxLen);
	LoadString (IDS_UNIT_KM,      g_strUnitKm,      c_cUnitMaxLen);
	LoadString (IDS_UNIT_NMILE,   g_strUnitNMile,   c_cUnitMaxLen);
	LoadString (IDS_UNIT_SMILE,   g_strUnitSMile,   c_cUnitMaxLen);
	LoadString (IDS_UNIT_MPS,     g_strUnitMps,     c_cUnitMaxLen);
	LoadString (IDS_UNIT_FPS,     g_strUnitFps,     c_cUnitMaxLen);
	LoadString (IDS_UNIT_KMH,     g_strUnitKmh,     c_cUnitMaxLen);
	LoadString (IDS_UNIT_KNOT,    g_strUnitKnot,    c_cUnitMaxLen);
	LoadString (IDS_UNIT_MPH,     g_strUnitMph,     c_cUnitMaxLen);
	LoadString (IDS_UNIT_M2,      g_strUnitM2,      c_cUnitMaxLen);
	LoadString (IDS_UNIT_KM2,     g_strUnitKm2,     c_cUnitMaxLen);
	LoadString (IDS_UNIT_FT2,     g_strUnitFt2,     c_cUnitMaxLen);
	LoadString (IDS_UNIT_MILE2,   g_strUnitMile2,   c_cUnitMaxLen);
	LoadString (IDS_UNIT_ACRE,    g_strUnitAcre,    c_cUnitMaxLen);
	LoadString (IDS_UNIT_HECTARE, g_strUnitHectare, c_cUnitMaxLen);
}

/////////////////////////////////////

void PrintInt (TCHAR * _strOut, int _nValue) {
	if (_nValue < 1000)
		::_stprintf (_strOut, _NL ("%d"), _nValue);
	else if (_nValue < 1000000)
		::_stprintf (_strOut, _NL ("%d %03d"), _nValue / 1000, _nValue % 1000);
	else {
		_nValue = (_nValue + 500)/1000;
		::_stprintf (_strOut, _NL ("%d %03d 000"), _nValue / 1000, _nValue % 1000);
	}
}

void PrintReal (TCHAR * _strOut, float _fValue) {
	if (_fValue < 1) {
		if (_fValue == 0)
			::_tcscpy (_strOut, _NL ("0"));
		else if (_fValue >= 0.1)
			::_stprintf (_strOut, _NL ("%0.3f"), _fValue);
		else
			::_stprintf (_strOut, _NL ("%0.4f"), _fValue);
	} else if (_fValue < 100)
		::_stprintf (_strOut, _NL ("%0.2f"), _fValue);
	else if (_fValue < 1000)
		::_stprintf (_strOut, _NL ("%0.1f"), _fValue);
	else
		PrintInt (_strOut, fast_floor (_fValue + .5f));
}

/////////////////////////////////////

float GetElevationUnitInM (TCHAR * _strUnits) {
	switch (g_ElevationUnit) {
		case unMeters:
			::_tcscpy (_strUnits, g_strUnitM);
			return 1;

		case unFeet:
			::_tcscpy (_strUnits, g_strUnitFt);
			return 1/c_feet_per_m;

		default:
			assert (false);
			return 1;
	}
}

static
size_t _PrintU (char * _strOut, unsigned long _u) {
	char buf [16];
	buf [15] = '\0';
	for (size_t c = 14; c > 0; -- c) {
		buf [c] = '0' + (_u % 10);
		_u /= 10;
		if (_u == 0) {
			::strcpy (_strOut, buf + c);
			return 15 - c;
		}
	}
	assert (false);
	return 0;
}

void PrintElevation (char * _strOut, TCHAR * _strUnitsOut, float _fElevation, elevation_units_t _FromUnit, bool _bInteger) {
	switch (g_ElevationUnit) {
		case unMeters:
			if (_strUnitsOut)
				::_tcscpy (_strUnitsOut, g_strUnitM);

			if (_FromUnit == unFeet)
				_fElevation /= c_feet_per_m;
			else
				assert (_FromUnit == unMeters);
			break;

		case unFeet:
			if (_strUnitsOut)
				::_tcscpy (_strUnitsOut, g_strUnitFt);

			if (_FromUnit == unMeters)
				_fElevation *= c_feet_per_m;
			else
				assert (_FromUnit == unFeet);
			break;

		default:
			assert (false);
	}

	if (_fElevation < 0) {
		_fElevation = -_fElevation;
		* (_strOut ++) = '-';
	}

	if (_bInteger) {
		_strOut += _PrintU (_strOut, fast_floor (_fElevation + .5f));
	} else {
		const unsigned uInt  = fast_floor (_fElevation + .05f);
		const unsigned uFrac = fast_floor ((_fElevation - uInt)*10 + .5f);
		_strOut += _PrintU (_strOut, uInt);
		* (_strOut ++) = '.';
		* (_strOut ++) = uFrac + '0';
		* (_strOut ++) = '\0';
	}

//	::strcpy (_strOut, pUnit);
}

void PrintDepth (TCHAR * _strOut, TCHAR * _strUnitsOut, float _fDepthInM) {
	// TODO: support fathoms.
	PrintElevation (_strOut, _strUnitsOut, _fDepthInM, unMeters, false);
}

/////////////////////////////////////////////////////

float GetDistanceUnitInKM (TCHAR * _strUnits) {
	switch (g_DistanceUnit) {
		case unMetric:
			::_tcscpy (_strUnits, g_strUnitKm);
			return 1000/1000;

		case unNautical: 
			::_tcscpy (_strUnits, g_strUnitNMile);
			return c_m_per_nmile/1000;

		case unStatute:
			::_tcscpy (_strUnits, g_strUnitSMile);
			return c_m_per_smile/1000;

		default:
			assert (false);
			return 1;
	}
}

void PrintDistance (TCHAR * _strOut, TCHAR * _strUnitsOut, float _fDistanceInM) {
	assert (_fDistanceInM >= 0);

	switch (g_DistanceUnit) {
		case unMetric:
			if (_fDistanceInM < 10) {
				::_stprintf (_strOut, _NL ("%0.1f"), _fDistanceInM);
				::_tcscpy (_strUnitsOut, g_strUnitM);
			} else if (_fDistanceInM < 1000) {
				::_stprintf (_strOut, _NL ("%d"), fast_floor (_fDistanceInM + .5f));
				::_tcscpy (_strUnitsOut, g_strUnitM);
			} else {
				PrintReal (_strOut, _fDistanceInM/1000);
				::_tcscpy (_strUnitsOut, g_strUnitKm);
			}
			break;

		case unNautical: 
		case unStatute: {
			const float fDistanceInMiles = g_DistanceUnit == unNautical ? _fDistanceInM/c_m_per_nmile : _fDistanceInM/c_m_per_smile;
			if (fDistanceInMiles < 1) {
				const float fDistanceInFeet = _fDistanceInM*c_feet_per_m;
				if (fDistanceInFeet < 10) {
					::_stprintf (_strOut, _NL ("%0.1f"), fDistanceInFeet);
					::_tcscpy (_strUnitsOut, g_strUnitFt);
				} else {
					::_stprintf (_strOut, _NL ("%d"), fast_floor (fDistanceInFeet + .5f));
					::_tcscpy (_strUnitsOut, g_strUnitFt);
				}
			} else if (fDistanceInMiles < 100) {
				PrintReal (_strOut, fDistanceInMiles);
				::_tcscpy (_strUnitsOut, g_DistanceUnit == unNautical ? g_strUnitNMile : g_strUnitSMile);
			}
			break;
		}

		default:
			assert (false);
	}
}

void PrintArea (TCHAR * _strOut, TCHAR * _strUnitsOut, float _fAreaInM2) {
	switch (g_AreaUnit) {
		case unSquareMeters:
			if (_fAreaInM2 < 100000) {
				PrintInt (_strOut, fast_floor (_fAreaInM2 + .5f));
				::_tcscpy (_strUnitsOut, g_strUnitM2);
			} else {
				PrintReal (_strOut, _fAreaInM2/1e6);
				::_tcscpy (_strUnitsOut, g_strUnitKm2);
			}
			break;

		case unHectares:
			PrintReal (_strOut, _fAreaInM2/1e4);
			::_tcscpy (_strUnitsOut, g_strUnitHectare);
			break;

		case unSquareFeet: {
			const float fAreaInSquareMiles = _fAreaInM2/(c_m_per_smile*c_m_per_smile);
			if (fAreaInSquareMiles < 0.01) {
				PrintInt (_strOut, fast_floor (_fAreaInM2*c_feet_per_m*c_feet_per_m));
				::_tcscpy (_strUnitsOut, g_strUnitFt2);
			} else {
				PrintReal (_strOut, fAreaInSquareMiles);
				::_tcscpy (_strUnitsOut, g_strUnitMile2);
			}
			break;
		}

		case unAcres:
			PrintReal (_strOut, _fAreaInM2/0.4047e4);
			::_tcscpy (_strUnitsOut, g_strUnitAcre);
			break;

		default: assert (false);
	}
}

void PrintSpeed (TCHAR * _strOut, TCHAR * _strUnitsOut, float _fSpeedInKmh) {
	switch (g_DistanceUnit) {
		case unMetric:
			::_stprintf (_strOut, _NL ("%0.1f"), _fSpeedInKmh);
			::_tcscpy (_strUnitsOut, g_strUnitKmh);
			break;

		case unNautical:
			::_stprintf (_strOut, _NL ("%0.1f"), _fSpeedInKmh/c_km_per_nmile);
			::_tcscpy (_strUnitsOut, g_strUnitKnot);
			break;

		case unStatute:
			::_stprintf (_strOut, _NL ("%0.1f"), _fSpeedInKmh/c_km_per_smile);
			::_tcscpy (_strUnitsOut, g_strUnitMph);
			break;

		default:
			assert (false);
	}
}

void PrintVSpeed (TCHAR * _strOut, TCHAR * _strUnitsOut, float _fSpeedInMps) {
	switch (g_DistanceUnit) {
		case unMetric:
			::_stprintf (_strOut, _NL ("%0.1f"), _fSpeedInMps);
			::_tcscpy (_strUnitsOut, g_strUnitMps);
			break;

		case unNautical:
		case unStatute:
			::_stprintf (_strOut, _NL ("%0.1f"), _fSpeedInMps*c_feet_per_m);
			::_tcscpy (_strUnitsOut, g_strUnitFps);
			break;

		default:
			assert (false);
	}
}

void PrintTemperature (TCHAR * _strOut, TCHAR * _strUnitsOut, float _fTemperatureInC) {
	::_stprintf (_strOut,      _NL ("%0.1f"), _fTemperatureInC);
	::_stprintf (_strUnitsOut, _NL ("%sC"),   g_strDegreeSign);
}

void PrintDuration (TCHAR * _strOut, DATE _dt) {
	const size_t cHours   = fast_floor (_dt*24);
	const size_t cMinutes = fast_floor ((_dt - cHours/24.)*24*60);
	const size_t cSeconds = fast_floor ((_dt - cHours/24. - cMinutes/24./60.)*24*60*60);
	::_stprintf (_strOut, _NL ("%0d:%02d:%02d"), cHours, cMinutes, cSeconds);
}

static const UINT c_uiCardinallityIds [8] = {
	IDS_CARDINALITY_N,
	IDS_CARDINALITY_NE,
	IDS_CARDINALITY_E,
	IDS_CARDINALITY_SE,
	IDS_CARDINALITY_S,
	IDS_CARDINALITY_SW,
	IDS_CARDINALITY_W,
	IDS_CARDINALITY_NW,
};

void PrintAzimuth (TCHAR * _strOut, float _fAzimuthInDeg) {
	assert (0 <= _fAzimuthInDeg && _fAzimuthInDeg < 360);

	switch (g_AzimuthFormat) {
		case azDegrees:
			::_stprintf (_strOut, _NL ("%d%s true"), fast_floor_d (_fAzimuthInDeg + .5f), g_strDegreeSign);
			break;

		case azCardinality: {
			const size_t cCardinalityIndex = fast_floor ((_fAzimuthInDeg + 22.5f)/45);
			LoadString (c_uiCardinallityIds [cCardinalityIndex % 8], _strOut, 10);
			break;
		}

		default:
			assert (false);
	}
}

/////////////////////////////////////////////////////

static
size_t _PrintArc (char * _strOut, float _f, const char _arc_positive, const char _arc_negative) {
	if (g_PositionFormat == pfDecimal)
		return ::sprintf (_strOut, "%0.6f", _f);

	char sign = _arc_positive;
	if (_f < 0) {
		sign = _arc_negative;
		_f = - _f;
	}

	if (g_PositionFormat == pfDegrees) {
		return ::sprintf (_strOut, "%c%0.6f%s", sign, _f, g_strDegreeSign);
	} else {
		const unsigned nDeg = static_cast<unsigned> (fast_floor (_f));
		_f = _f - nDeg;  // now _f contains fractional part
		_f *= 60;

		if (g_PositionFormat == pfMinutes) {
			return ::sprintf (_strOut, "%c%d%s%06.3f'", sign, nDeg, g_strDegreeSign, _f);
		} else if (g_PositionFormat == pfSeconds) {
			
			const unsigned nMinutes = (unsigned) fast_floor (_f);
			const float fSec = (_f - nMinutes)*60;
			return ::sprintf (_strOut, "%c%d%s%02d'%04.1f\"", sign, nDeg, g_strDegreeSign, nMinutes, fSec);
		}
	}

	assert (false);
	return 0;
}

void PrintPosition (char * _strOut, const point_t & _pt) {
	const size_t c = _PrintArc (_strOut, _pt.y, 'N', 'S');
	_strOut [c] = ' ';
	_PrintArc (_strOut + c + 1, _pt.x, 'E', 'W');
}

void PrintLat (char * _strOut, float _y) {
	_PrintArc (_strOut, _y, 'N', 'S');
}

void PrintLon (char * _strOut, float _x) {
	_PrintArc (_strOut, _x, 'E', 'W');
}
